package org.mx.service.server.websocket;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.frames.PongFrame;
import org.mx.spring.utils.SpringContextHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 标准化的Websocket对象定义，通用结构，便于后续业务定制自有的WebSocket通信对象。
 */
@WebSocket
public class SimpleWsObject {
	private static final Logger logger = LoggerFactory.getLogger(SimpleWsObject.class);
	private WsLifeCircle lifeCircle = null;

	/**
	 * 默认的构造函数
	 */
	public SimpleWsObject() {
		super();
	}

	/**
	 * 构造函数
	 *
	 * @param lifeCircle Websocket会话监听器
	 */
	public SimpleWsObject(WsLifeCircle lifeCircle) {
		this();
		this.lifeCircle = lifeCircle;
	}

	/**
	 * 触发连接
	 * 
	 * @param session 会话
	 */
	@OnWebSocketConnect
	public final void onConnect(Session session) {
		WsConnectKey connectKey = new WsConnectKey(session);
		if (logger.isInfoEnabled()) {
			logger.info(String.format("On connect: %s.", connectKey.toString()));
		}
		WsSessionManager manager = SpringContextHolder.getBean(WsSessionManager.class);
		if (manager != null) {
			manager.addSession(session);
		}
		if (lifeCircle != null) {
			lifeCircle.afterConnect(connectKey);
		}
	}

	/**
	 * 触发关闭
	 * 
	 * @param session 会话
	 * @param code    关闭代码
	 * @param reason  关闭原因
	 */
	@OnWebSocketClose
	public final void onClose(Session session, int code, String reason) {
		WsConnectKey connectKey = new WsConnectKey(session);
		if (lifeCircle != null) {
			lifeCircle.beforeClose(connectKey);
		}
		if (logger.isInfoEnabled()) {
			logger.info(String.format("On connect: %s.", connectKey.toString()));
		}
		WsSessionManager manager = SpringContextHolder.getBean(WsSessionManager.class);
		if (manager != null) {
			manager.removeSession(session);
		}
		if (lifeCircle != null) {
			lifeCircle.afterClose(connectKey, code, reason);
		}
	}

	/**
	 * 触发错误
	 * 
	 * @param session   会话
	 * @param throwable 发生的错误
	 */
	@OnWebSocketError
	public final void onError(Session session, Throwable throwable) {
		WsConnectKey connectKey = new WsConnectKey(session);
		if (logger.isInfoEnabled()) {
			logger.info(String.format("On error: %s.", connectKey.toString()));
		}
		if (lifeCircle != null) {
			lifeCircle.hasError(connectKey, throwable);
		}
	}

	/**
	 * 触发Frame通信
	 * 
	 * @param session 会话
	 * @param frame   Frame
	 */
	@OnWebSocketFrame
	public final void onFrame(Session session, Frame frame) {
		WsConnectKey connectKey = new WsConnectKey(session);
		if (frame instanceof PongFrame) {
			WsSessionManager manager = SpringContextHolder.getBean(WsSessionManager.class);
			if (manager != null) {
				manager.pong(connectKey);
			}
			if (logger.isDebugEnabled()) {
				logger.debug(String.format("Pong frame: %s, payload: %s.", connectKey.toString(),
						((PongFrame) frame).getPayloadAsUTF8()));
			}
		} else {
			if (logger.isInfoEnabled()) {
				logger.info(String.format("On frame[%s]: %s.", frame.getType().name(), connectKey.toString()));
			}
		}
	}

	/**
	 * 触发二进制消息通信
	 * 
	 * @param session     会话
	 * @param inputStream 输入流
	 */
	@OnWebSocketMessage
	public final void onBinaryMessage(Session session, InputStream inputStream) {
		WsConnectKey connectKey = new WsConnectKey(session);
		if (logger.isInfoEnabled()) {
			logger.info(String.format("On binary message: %s.", connectKey.toString()));
		}
		byte[] buffer = new byte[4096];
		int len;
		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
			while ((len = inputStream.read(buffer)) > 0) {
				baos.write(buffer, 0, len);
			}
			if (lifeCircle != null) {
				lifeCircle.hasBinary(connectKey, baos.toByteArray());
			}
		} catch (IOException ex) {
			if (logger.isErrorEnabled()) {
				logger.error(String.format("Read byte buffer fail from %s.", connectKey.toString()), ex);
			}
		}
	}

	/**
	 * 触发文本消息通信
	 * 
	 * @param session 会话
	 * @param message 文本消息
	 */
	@OnWebSocketMessage
	public final void onTextMessage(Session session, String message) {
		WsConnectKey connectKey = new WsConnectKey(session);
		if (logger.isInfoEnabled()) {
			logger.info(String.format("On text message: %s.", connectKey.toString()));
		}
		if (lifeCircle != null) {
			lifeCircle.hasText(connectKey, message);
		}
	}
}
